﻿// -------------------------------------------------------------------------
//    @FileName         :    MapDataEx.cpp
//    @Author           :    gaoyi
//    @Date             :    2025-03-24
//    @Email			:    445267987@qq.com
//    @Module           :    MapDataEx
//
// -------------------------------------------------------------------------

#include "MapDataEx.h"

#include <NFLogicCommon/NFCommMacros.h>
#include <NFLogicCommon/ParseUtil.h>

#include "proto_common_map.nanopb.h"
#include "NFLogicCommon/NFNavmeshUtil.h"

#include "NFComm/NFCore/NFPlatform.h"
#include "Detour/Include/DetourNavMesh.h"
#include "Detour/Include/DetourNavMeshQuery.h"
#include "Detour/Include/DetourCommon.h"

int32_t g_air_wall_index = 0;

template <typename T>
static int ParseStringToVector3(const char* pString, T* pVector3)
{
    char value_buff[1024];
    strncpy(value_buff, pString, sizeof(value_buff));
    char* pStart = value_buff;
    char* pNext = strstr(pStart, ",");
    CHECK_NULL(0, pNext);
    *pNext = 0;
    pVector3->x = atof(pStart) * VECTOR3_MULTI;

    pStart = pNext + 1;
    pNext = strstr(pStart, ",");
    CHECK_NULL(0, pNext);
    *pNext = 0;
    pVector3->y = atof(pStart) * VECTOR3_MULTI;

    pStart = pNext + 1;
    pVector3->z = atof(pStart) * VECTOR3_MULTI;

    return 0;
}

MapDataEx::MapDataEx()
{
    if (EN_OBJ_MODE_INIT == NFShmMgr::Instance()->GetCreateMode())
    {
        CreateInit();
    }
    else
    {
        ResumeInit();
    }
}

MapDataEx::~MapDataEx()
{
}

int MapDataEx::CreateInit()
{
    return 0;
}

int MapDataEx::ResumeInit()
{
    return 0;
}

int MapDataEx::Load(NFResDb* pDB)
{
    return 0;
}

int MapDataEx::CheckWhenAllDataLoaded()
{
    m_astSpawnGroupPool.clear();
    m_astSpawnGroupPool.resize(m_astSpawnGroupPool.max_size());
    m_iFreeGroup = 0;
    m_astSpawnNpcPool.clear();
    m_astSpawnNpcPool.resize(m_astSpawnNpcPool.max_size());
    m_iFreeNpc = 0;
    memset(m_aiIndex, -1, sizeof(m_aiIndex));
    m_bLoadedNavmeshData = false;
    return 0;
}

int MapDataEx::LoadGroupNpc(MapDataDesc& mapDataDesc, int iMapDescID, const char* filename, const char* res_name)
{
    tinyxml2::XMLDocument* myDocument = new tinyxml2::XMLDocument();
    tinyxml2::XMLError error = myDocument->LoadFile(filename);
    CHECK_EXPR(error == 0, 0, "load file error:{}", error);
    tinyxml2::XMLElement* pRootElement = myDocument->RootElement();
    CHECK_NULL(0, pRootElement);

    const char* pValue = pRootElement->Attribute("SceneName");
    CHECK_NULL(0, pValue);
    mapDataDesc.scene_name = pValue;

    int iRet = 0;

    tinyxml2::XMLElement* pElement = pRootElement->FirstChildElement();
    for (; pElement && 0 == iRet; pElement = pElement->NextSiblingElement())
    {
        if (0 == strcmp(pElement->Value(), "SpawnData"))
        {
            iRet = ReadElementSpawnGroup(mapDataDesc, pElement);
        }

        CHECK_RET(iRet, "level:{} read:{} failed", iMapDescID, pElement->Value());
    }

    delete myDocument;
    LOGSVR_TRACE("load level:" << filename << " ok ");
    return iRet;
}

int MapDataEx::LoadLevel(MapDataDesc& mapDataDesc, int iMapDescID, const char* filename, const char* res_name)
{
    tinyxml2::XMLDocument* myDocument = new tinyxml2::XMLDocument();
    tinyxml2::XMLError error = myDocument->LoadFile(filename);
    CHECK_EXPR(error == 0, error, "load file:{} error:{}", filename, error);
    tinyxml2::XMLElement* pRootElement = myDocument->RootElement();
    CHECK_NULL(0, pRootElement);

    const char* pValue = pRootElement->Attribute("SceneName");
    CHECK_NULL(0, pValue);
    mapDataDesc.scene_name = pValue;

    mapDataDesc.hero_spawn_list_count = 0;
    g_air_wall_index = 0;

    int iRet = 0;

    tinyxml2::XMLElement* pElement = pRootElement->FirstChildElement();
    for (; pElement && 0 == iRet; pElement = pElement->NextSiblingElement())
    {
        if (0 == strcmp(pElement->Value(), "MovePlatformData"))
        {
            iRet = ReadElementMovePlatform(mapDataDesc, pElement);
        }
        else if (0 == strcmp(pElement->Value(), "Hero") || 0 == strcmp(pElement->Value(), "Monster"))
        {
            iRet = ReadElementSpawn(mapDataDesc, pElement);
        }
        else if (0 == strcmp(pElement->Value(), "Line"))
        {
            iRet = ReadElementLine(mapDataDesc, pElement);
        }
        else if (0 == strcmp(pElement->Value(), "SpawnData"))
        {
            iRet = ReadElementSpawnGroup(mapDataDesc, pElement);
        }
        else if (0 == strcmp(pElement->Value(), "StrongholdData"))
        {
            iRet = ReadElementStronghold(mapDataDesc, pElement);
        }
        else if (0 == strcmp(pElement->Value(), "CrystalData"))
        {
            iRet = ReadElementCrystalData(mapDataDesc, pElement);
        }
        else if (0 == strcmp(pElement->Value(), "WeatherSettings"))
        {
            iRet = ReadElementWeather(mapDataDesc, pElement);
        }
        else if (0 == strcmp(pElement->Value(), "BehaviorTreeData"))
        {
            iRet = ReadElementBehaviorBox(mapDataDesc, pElement);
        }
        else if (0 == strcmp(pElement->Value(), "Conditions"))
        {
            iRet = ReadElementLevelCondition(mapDataDesc, pElement);
        }

        CHECK_RET(iRet, "level[{}] read {} failed", iMapDescID, pElement->Value());
    }

    delete myDocument;
    LOGSVR_TRACE("load level:" << filename << " ok ");
    return iRet;
}

int MapDataEx::LoadScene(MapDataDesc& mapDataDesc, int iMapDescID, const char* pszMapDataPath, const char* scene_res_name)
{
    std::string szSceneFileName = NF_FORMAT("{}/scene/{}.xml", pszMapDataPath, scene_res_name);
    std::string szNavmeshDataFileName = NF_FORMAT("{}/scene/navmesh/{}.navmesh", pszMapDataPath, scene_res_name);

    tinyxml2::XMLDocument* myDocument = new tinyxml2::XMLDocument();
    tinyxml2::XMLError error = myDocument->LoadFile(szSceneFileName.c_str());
    CHECK_EXPR(error == 0, 0, "load file error:{}", error);
    tinyxml2::XMLElement* pRootElement = myDocument->RootElement();
    CHECK_NULL(0, pRootElement);

    int iRet = 0;
    mapDataDesc.standard_curvy_spline = true;

    tinyxml2::XMLElement* pElement = pRootElement->FirstChildElement();
    for (; pElement && 0 == iRet; pElement = pElement->NextSiblingElement())
    {
        if (0 == strcmp(pElement->Value(), "Path"))
        {
            iRet = ReadElementPath(mapDataDesc, pElement);
        }
        else if (0 == strcmp(pElement->Value(), "Info"))
        {
            const char* pValue = pElement->Attribute("min");
            CHECK_NULL(0, pValue);

            iRet = ParseStringToVector3(pValue, &mapDataDesc.min);
            CHECK_RET(iRet, "");

            pValue = pElement->Attribute("max");
            CHECK_NULL(0, pValue);

            iRet = ParseStringToVector3(pValue, &mapDataDesc.max);
        }
        CHECK_RET(iRet, "scene[{}] [{}] read {} failed", iMapDescID, szSceneFileName, pElement->Value());
    }
    CHECK_EXPR(mapDataDesc.curvy_spline.size() > 0, -3, "no curvy spline for map:{}", mapDataDesc.map_id);
    delete myDocument;

    return iRet;
}

int MapDataEx::LoadNavmeshData(MapDataDesc& mapDataDesc, const char* pszMapDataPath, const char* scene_res_name, bool m_bIsNavmeshLoaded)
{
    std::string szSceneFileName = NF_FORMAT("{}/scene/{}.xml", pszMapDataPath, scene_res_name);
    std::string szNavmeshDataFileName = NF_FORMAT("{}/scene/navmesh/{}.navmesh", pszMapDataPath, scene_res_name);

    LOGSVR_TRACE("--begin--map[" << mapDataDesc.map_id << "] scene_res_name:" << scene_res_name);

    bool bReMakeNavmesh = false;
    struct stat scene_statbuf, navmesh_statbuf;
    int iRet = stat(szSceneFileName.c_str(), &scene_statbuf);
    CHECK_EXPR(iRet != -1, -1, "get scene file[{}] change time err({})", szSceneFileName, errno);
    iRet = stat(szNavmeshDataFileName.c_str(), &navmesh_statbuf);
    if (iRet == -1)
    {
        bReMakeNavmesh = true;
    }

    iRet = ReadNavmeshData(mapDataDesc, pszMapDataPath, scene_res_name, bReMakeNavmesh, m_bIsNavmeshLoaded);

    LOGSVR_TRACE("--end--");
    return iRet;
}

int MapDataEx::ReadNavmeshData(MapDataDesc& mapDataDesc, const char* szPath, const char* res_name, bool bReMakeNavmesh, bool m_bIsNavmeshLoaded)
{
    CHECK_NULL(0, szPath);
    CHECK_NULL(0, res_name);

    std::string szNavmeshName = NF_FORMAT("{}/scene/navmesh/{}.navmesh", szPath, res_name);

    if (bReMakeNavmesh == true)
    {
        std::string szMapName = NF_FORMAT("{}.xml", res_name);
        int iRet = NFNavmeshUtil::MakeNavmesh(szPath, szMapName.c_str());
        CHECK_RET(iRet, "CNavmeshUtil::MakeNavmesh {}/{} failed", szPath, szMapName);
    }

    FILE* fp = fopen(szNavmeshName.c_str(), "r");
    CHECK_NULL(0, fp);
    fseek(fp, 0, SEEK_END);
    size_t uNavmeshDataSize = ftell(fp);

    fseek(fp, 0, SEEK_SET);

    void* mem = dtAlloc(uNavmeshDataSize, DT_ALLOC_PERM);
    CHECK_EXPR(mem, -1, "alloc mem err. size={}", uNavmeshDataSize);

    size_t size = fread(mem, 1, uNavmeshDataSize, fp);
    fclose(fp);
    CHECK_EXPR(size == (size_t)uNavmeshDataSize, -1, "read navmesh err({}). name={}", errno, szNavmeshName);

    if (mapDataDesc.navmesh_address != 0 && m_bIsNavmeshLoaded)
    {
        dtNavMeshQuery* pNavMeshQuery = (dtNavMeshQuery*)mapDataDesc.navmesh_address;
        dtNavMesh* pNavMesh = const_cast<dtNavMesh*>(pNavMeshQuery->getAttachedNavMesh());
        dtFreeNavMeshQuery(pNavMeshQuery);
        dtFreeNavMesh(pNavMesh);
    }
    mapDataDesc.navmesh_address = 0;

    dtNavMesh* m_navmesh = dtAllocNavMesh();
    CHECK_NULL(0, m_navmesh);
    dtStatus dtRet = m_navmesh->init((unsigned char*)(mem), uNavmeshDataSize, DT_TILE_FREE_DATA);
    if (dtRet != DT_SUCCESS)
    {
        //清除内存
        LOGSVR_ERROR("init m_navmesh wrong. map=" << res_name);
        dtFreeNavMesh(m_navmesh);
        return -1;
    }
    dtNavMeshQuery* pstNavmeshQuery = dtAllocNavMeshQuery();
    CHECK_NULL(0, pstNavmeshQuery);

    dtRet = pstNavmeshQuery->init(m_navmesh, 2048);
    if (dtRet != DT_SUCCESS)
    {
        //清除内存
        LOGSVR_ERROR("init pstNavmeshQuery wrong. map=" << res_name);

        dtFreeNavMeshQuery(pstNavmeshQuery);
        dtFreeNavMesh(m_navmesh);
        return -1;
    }

    mapDataDesc.navmesh_address = (uint64_t)(pstNavmeshQuery); //程序退出时清空

    return 0;
}

int MapDataEx::ReadElementSpawn(MapDataDesc& mapDataDesc, tinyxml2::XMLElement* pSpawnElement)
{
    mapDataDesc.hero_spawn_list_count = 0;
    memset(&mapDataDesc.hero_spawn_list, -1, sizeof(mapDataDesc.hero_spawn_list));

    tinyxml2::XMLElement* pElement = pSpawnElement->FirstChildElement();
    for (; pElement; pElement = pElement->NextSiblingElement())
    {
        const char* pValue = pElement->Attribute("Camp");
        CHECK_NULL(0, pValue);

        int iCampId = atoi(pValue);
        CHECK_EXPR(iCampId >= 0 && iCampId < (int)proto_ff::EN_CAMP_TYPE_MAX, -4, "invalid campid:{}", iCampId);

        HeroSpawnOne* pHeroSpawnOne = nullptr;

        if (iCampId == 3)
        {
            pValue = pElement->Attribute("index");
            CHECK_NULL(0, pValue);

            int iIdx = atoi(pValue);
            --iIdx;

            CHECK_EXPR(iIdx >= 0 && iIdx < (int)ARRAYSIZE(mapDataDesc.hero_spawn_list), -5, "invalid idx:{}", iIdx);

            pHeroSpawnOne = &mapDataDesc.hero_spawn_list[iIdx];
            mapDataDesc.hero_spawn_list_count = std::max(static_cast<int>(mapDataDesc.hero_spawn_list_count), iIdx + 1);

            CHECK_EXPR(pHeroSpawnOne->camp_id == -1, -6, "idx: {} is used", iIdx);
        }
        else
        {
            for (int i = 0; i < (int)ARRAYSIZE(mapDataDesc.hero_spawn_list); ++i)
            {
                if (mapDataDesc.hero_spawn_list[i].camp_id == -1)
                {
                    pHeroSpawnOne = &mapDataDesc.hero_spawn_list[i];
                    mapDataDesc.hero_spawn_list_count = std::max(static_cast<int>(mapDataDesc.hero_spawn_list_count), i + 1);
                    break;
                }
            }
        }
        CHECK_NULL(0, pHeroSpawnOne);

        pHeroSpawnOne->camp_id = iCampId;

        pValue = pElement->Attribute("X");
        CHECK_NULL(0, pValue);
        pHeroSpawnOne->x = atof(pValue) * VECTOR3_MULTI;

        pValue = pElement->Attribute("Y");
        CHECK_NULL(0, pValue);
        pHeroSpawnOne->y = atof(pValue) * VECTOR3_MULTI;

        pValue = pElement->Attribute("Z");
        CHECK_NULL(0, pValue);
        pHeroSpawnOne->z = atof(pValue) * VECTOR3_MULTI;

        pValue = pElement->Attribute("StartDirection");
        CHECK_NULL(0, pValue);
        pHeroSpawnOne->direction = atoi(pValue);
    }

    return 0;
}

int MapDataEx::ReadElementMovePlatform(MapDataDesc& mapDataDesc, tinyxml2::XMLElement* pMovePlatformElement)
{
    LOGSVR_TRACE("--begin--");
    tinyxml2::XMLElement* pElement = pMovePlatformElement->FirstChildElement();
    for (; pElement; pElement = pElement->NextSiblingElement())
    {
        CHECK_EXPR(mapDataDesc.move_platform.size() < mapDataDesc.move_platform.max_size(), -3, "too many move_platform for map:{}", mapDataDesc.map_id);
        mapDataDesc.move_platform.push_back(MovePlatform());
        MovePlatform& movePlatform = mapDataDesc.move_platform.back();
        movePlatform.id = mapDataDesc.move_platform.size();
        const char* pValue = pElement->Attribute("pos");
        CHECK_NULL(0, pValue);

        int iRet = ParseStringToVector3(pValue, &movePlatform.pos);
        CHECK_RET(iRet, "ParseStringToVector3 failed.");

        pValue = pElement->Attribute("angle");
        CHECK_NULL(0, pValue);
        movePlatform.angle = atoi(pValue);

        pValue = pElement->Attribute("name");
        CHECK_NULL(0, pValue);
        movePlatform.name = pValue;

        pValue = pElement->Attribute("speed");
        CHECK_NULL(0, pValue);
        movePlatform.speed = atof(pValue);

        pValue = pElement->Attribute("lineName");
        CHECK_NULL(0, pValue);
        movePlatform.line_name = pValue;

        pValue = pElement->Attribute("startPointIndex");
        CHECK_NULL(0, pValue);
        movePlatform.start_point_index = atoi(pValue);

        pValue = pElement->Attribute("pathType");
        CHECK_NULL(0, pValue);
        movePlatform.path_type = atoi(pValue);
    }

    LOGSVR_FATAL("--end--");

    return 0;
}

int MapDataEx::ReadElementLine(MapDataDesc& mapDataDesc, tinyxml2::XMLElement* pSpawnElement)
{
    LOGSVR_TRACE("--begin--");
    tinyxml2::XMLElement* pElement = pSpawnElement->FirstChildElement();
    for (; pElement; pElement = pElement->NextSiblingElement())
    {
        CHECK_EXPR(mapDataDesc.line.size() < mapDataDesc.line.max_size(), -3, "too many line for map:{}", mapDataDesc.map_id);
        mapDataDesc.line.push_back(Line());
        Line& line = mapDataDesc.line.back();
        const char* pValue = pElement->Attribute("name");
        CHECK_NULL(0, pValue);
        line.name = pValue;

        tinyxml2::XMLElement* pChildElement = pElement->FirstChildElement();
        for (; pChildElement;
               pChildElement = pChildElement->NextSiblingElement())
        {
            CHECK_EXPR(line.point.size() < line.point.max_size(), -3, "too many point for line:{}", mapDataDesc.map_id);
            line.point.push_back(LinePoint());
            LinePoint& point = line.point.back();

            pValue = pChildElement->Attribute("pos");
            CHECK_NULL(0, pValue);

            int iRet = ParseStringToVector3(pValue, &point.pos);
            CHECK_RET(iRet, "ParseStringToVector3 failed.");

            pValue = pChildElement->Attribute("eventType");
            CHECK_NULL(0, pValue);
            point.event_type = atoi(pValue);

            pValue = pChildElement->Attribute("dataParam1");
            CHECK_NULL(0, pValue);
            point.data_param.push_back(atoi(pValue));

            pValue = pChildElement->Attribute("dataParam2");
            CHECK_NULL(0, pValue);
            point.data_param.push_back(atoi(pValue));

            pValue = pChildElement->Attribute("dataParam3");
            CHECK_NULL(0, pValue);
            point.data_param.push_back(atoi(pValue));

            pValue = pChildElement->Attribute("dataParam4");
            CHECK_NULL(0, pValue);
            point.data_param.push_back(atoi(pValue));

            pValue = pChildElement->Attribute("dataParam5");
            CHECK_NULL(0, pValue);
            point.data_param.push_back(atoi(pValue));
        }
    }

    LOGSVR_FATAL("--end--");

    return 0;
}

int MapDataEx::ReadElementPath(MapDataDesc& mapDataDesc, tinyxml2::XMLElement* pSpawnElement)
{
    tinyxml2::XMLElement* pElement = pSpawnElement->FirstChildElement();
    for (; pElement; pElement = pElement->NextSiblingElement())
    {
        if (0 != strcmp(pElement->Value(), "CurvySpline_New"))
            continue;
        CHECK_EXPR(mapDataDesc.curvy_spline.size() < mapDataDesc.curvy_spline.max_size(), -3, "too many curvy spline for map:{}", mapDataDesc.map_id);

        mapDataDesc.curvy_spline.push_back(CurvySpline());
        CurvySpline& curvyspline = mapDataDesc.curvy_spline.back();

        tinyxml2::XMLElement* pChildElement = pElement->FirstChildElement();
        for (; pChildElement;
               pChildElement = pChildElement->NextSiblingElement())
        {
            if (0 == strcmp("Box", pChildElement->Value()))
            {
                const char* pValue = pChildElement->Attribute("Min");
                CHECK_NULL(0, pValue);

                int iRet = ParseStringToVector3(pValue, &curvyspline.box.min);
                CHECK_RET(iRet, "ParseStringToVector3 failed.");

                pValue = pChildElement->Attribute("Max");
                CHECK_NULL(0, pValue);
                iRet = ParseStringToVector3(pValue, &curvyspline.box.max);
                CHECK_RET(iRet, "ParseStringToVector3 failed.");
            }
            else if (0 == strcmp("CurvySplineSegment", pChildElement->Value()))
            {
                CHECK_EXPR(curvyspline.cp.size() < curvyspline.cp.max_size(), -3, "too many curvy_spline segment for map:{}", mapDataDesc.map_id);

                curvyspline.cp.push_back(CurvySplineSegment());
                CurvySplineSegment& curvySplineSegment = curvyspline.cp.back();
                const char* pValue = pChildElement->Attribute("pos");
                CHECK_NULL(0, pValue);

                int iRet = ParseStringToVector3(pValue, &curvySplineSegment.pos);
                CHECK_RET(iRet, "ParseStringToVector3 failed.");

                pValue = pChildElement->Attribute("name");
                CHECK_NULL(0, pValue);
                curvySplineSegment.name = pValue;
            }
            else if (0 == strcmp("LineItem", pChildElement->Value()))
            {
                CHECK_EXPR(curvyspline.approximation.size() < curvyspline.approximation.max_size(), -3, "too many curvy_spline approximation for map:{}", mapDataDesc.map_id);

                curvyspline.approximation.push_back(CurvySplineApproximation());
                CurvySplineApproximation& curvySplineApproximation = curvyspline.approximation.back();
                const char* pValue = pChildElement->Attribute("pos");
                CHECK_NULL(0, pValue);

                int iRet = ParseStringToVector3(pValue, &curvySplineApproximation.pos);
                CHECK_RET(iRet, "ParseStringToVector3 failed.");

                pValue = pChildElement->Attribute("tangent");
                CHECK_NULL(0, pValue);

                iRet = ParseStringToVector3(pValue, &curvySplineApproximation.tangent);
                CHECK_RET(iRet, "ParseStringToVector3 failed.");
                if (curvySplineApproximation.tangent.x != VECTOR3_MULTI
                    || curvySplineApproximation.tangent.y != 0
                    || curvySplineApproximation.tangent.z != 0)
                {
                    mapDataDesc.standard_curvy_spline = false;
                }
            }
        }
    }

    return 0;
}

int MapDataEx::ReadElementSpawnGroup(MapDataDesc& mapDataDesc, tinyxml2::XMLElement* pElement)
{
    LOGSVR_TRACE("--begin--");

    mapDataDesc.abyss_group_idx = INVALID_ID;
    mapDataDesc.spawn_group_start_idx = Instance()->m_iFreeGroup;
    tinyxml2::XMLElement* pElementGroup = pElement->FirstChildElement();
    for (; pElementGroup; pElementGroup = pElementGroup->NextSiblingElement())
    {
        //CHECK_EXPR(mapDataDesc.spawn_group_size < (int)ARRAYSIZE(mapDataDesc.spawn_group), -3, "too many spawn group for map:" << mapDataDesc.map_id);
        //proto_ff_s::SpawnGroup_s* pstSpawnGroup = mapDataDesc.spawn_group + (mapDataDesc.spawn_group_size++);
        CHECK_EXPR(Instance()->m_iFreeGroup < (int)Instance()->m_astSpawnGroupPool.size(), -3, "too many spawn group for map:{}", mapDataDesc.map_id);
        SpawnGroup* pstSpawnGroup = &Instance()->m_astSpawnGroupPool[Instance()->m_iFreeGroup++];

        const char* pValue = pElementGroup->Attribute("ID");
        CHECK_NULL(0, pValue);
        pstSpawnGroup->id = atoi(pValue);

        if (pstSpawnGroup->id == 0)
        {
            Instance()->m_iFreeGroup--;

            LOGSVR_ERROR("invalid group id 0: " << " map desc id: " << mapDataDesc.map_id);
            continue;
        }

        pValue = pElementGroup->Attribute("upper_limit_num");
        if (pValue)
        {
            pstSpawnGroup->upper_limit_num = atoi(pValue);
        }
        else
        {
            pstSpawnGroup->upper_limit_num = 0;
        }

        pValue = pElementGroup->Attribute("hatred");
        if (pValue)
        {
            pstSpawnGroup->share_hate = atoi(pValue);
        }
        else
        {
            pstSpawnGroup->share_hate = 0;
        }

        pValue = pElementGroup->Attribute("issequence");
        if (pValue)
        {
            pstSpawnGroup->sequence = atoi(pValue);
        }
        else
        {
            pstSpawnGroup->sequence = false;
        }

        pValue = pElementGroup->Attribute("count_total");
        if (pValue)
        {
            pstSpawnGroup->count_total = atoi(pValue);
        }
        else
        {
            pstSpawnGroup->count_total = 0;
        }

        std::string strCon[] = {"Num", "Camp", "Trigger_On", "Group_Spawn", "Group_Remain", "Group_Exist"};
        std::string strTrig[] = {"NULL", "Born", "Interval", "Campchange", "Death", "Passive", "Abyssborn"};

        pstSpawnGroup->SpawnTrig.resize(ARRAYSIZE(strTrig));
        pstSpawnGroup->SpawnCon.resize(ARRAYSIZE(strCon));

        for (size_t i = 0; i < pstSpawnGroup->SpawnTrig.size(); i++)
        {
            //默认所有触发器无效
            pstSpawnGroup->SpawnTrig[i].bValid = false;
            pstSpawnGroup->SpawnTrig[i].eSpawnTrig = static_cast<enSpawnTrigger>(i);
        }
        for (size_t i = 0; i < pstSpawnGroup->SpawnCon.size(); i++)
        {
            //默认所有条件无效
            pstSpawnGroup->SpawnCon[i].bValid = false;
            pstSpawnGroup->SpawnCon[i].eSpawnCon = static_cast<enSpawnCondition>(i);
        }

        tinyxml2::XMLElement* pElementSon = pElementGroup->FirstChildElement();
        for (; pElementSon; pElementSon = pElementSon->NextSiblingElement())
        {
            if (0 == strcmp(pElementSon->Value(), "SpawnConditions"))
            {
                tinyxml2::XMLElement* pElementCon = pElementSon->FirstChildElement();
                for (; pElementCon;
                       pElementCon = pElementCon->NextSiblingElement())
                {
                    const char* pValue = pElementCon->Attribute("Condition");
                    CHECK_NULL(0, pValue);

                    int iContype = -1;
                    for (int k = 0; k < static_cast<int>(ARRAYSIZE(strCon)); k++)
                    {
                        if (0 == strcmp(pValue, strCon[k].c_str()))
                        {
                            iContype = k;
                            break;
                        }
                    }
                    if (iContype == -1)
                        continue;

                    SpawnCondition* pstCon = &pstSpawnGroup->SpawnCon[iContype];

                    pstCon->eSpawnCon = static_cast<enSpawnCondition>(iContype);

                    pValue = pElementCon->Attribute("Param");
                    if (pValue)
                        pstCon->param0 = atoi(pValue);

                    pValue = pElementCon->Attribute("Param0");
                    if (pValue)
                        pstCon->param0 = atoi(pValue);

                    pValue = pElementCon->Attribute("Param1");
                    if (pValue)
                        pstCon->param1 = atoi(pValue);

                    pValue = pElementCon->Attribute("Param2");
                    if (pValue)
                        pstCon->param2 = atoi(pValue);

                    pValue = pElementCon->Attribute("Param3");
                    if (pValue)
                        pstCon->param3 = atoi(pValue);

                    pstCon->bValid = true;
                }

                if (pstSpawnGroup->SpawnCon[proto_ff::ESPC_NUM].bValid == false || pstSpawnGroup->SpawnCon[proto_ff::ESPC_NUM].param0 == 0)
                {
                    //默认个数为1
                    pstSpawnGroup->SpawnCon[proto_ff::ESPC_NUM].bValid = true;
                    pstSpawnGroup->SpawnCon[proto_ff::ESPC_NUM].param0 = 1;
                }
                if (pstSpawnGroup->SpawnCon[proto_ff::ESPC_TRIG_ON].bValid == false)
                {
                    //默认开启
                    pstSpawnGroup->SpawnCon[proto_ff::ESPC_TRIG_ON].bValid = true;
                    pstSpawnGroup->SpawnCon[proto_ff::ESPC_TRIG_ON].param0 = 1;
                }
            }
            else if (0 == strcmp(pElementSon->Value(), "SpawnTriggers"))
            {
                //重复同一类触发器会覆盖
                tinyxml2::XMLElement* pElementTrig = pElementSon->FirstChildElement();
                for (; pElementTrig;
                       pElementTrig = pElementTrig->NextSiblingElement())
                {
                    const char* pValue = pElementTrig->Attribute("Trigger");
                    CHECK_NULL(0, pValue);

                    int iTrigType = -1;
                    for (int k = 0; k < static_cast<int>(ARRAYSIZE(strTrig)); k++)
                    {
                        if (0 == strcmp(pValue, strTrig[k].c_str()))
                        {
                            iTrigType = k;
                            break;
                        }
                    }
                    if (iTrigType == -1)
                        continue;

                    SpawnTrigger* pstTrig = &pstSpawnGroup->SpawnTrig[iTrigType];
                    pstTrig->eSpawnTrig = static_cast<enSpawnTrigger>(iTrigType);

                    pValue = pElementTrig->Attribute("Param0");
                    CHECK_NULL(0, pValue);
                    pstTrig->param0 = atoi(pValue);

                    pValue = pElementTrig->Attribute("Param1");
                    CHECK_NULL(0, pValue);
                    pstTrig->param1 = atoi(pValue);

                    pstTrig->bValid = true;

                    //只有深渊柱才用这种条件
                    if (pstTrig->eSpawnTrig == ESPT_ABYSS_BORN)
                    {
                        CHECK_EXPR(mapDataDesc.abyss_group_idx == INVALID_ID, -1, "already have abyss: {}", mapDataDesc.abyss_group_idx);

                        mapDataDesc.abyss_group_idx = Instance()->m_iFreeGroup - 1;
                    }
                }
            }
            else if (0 == strcmp(pElementSon->Value(), "SpawnBuff"))
            {
                SpawnBuff* pSpawnBuff = &pstSpawnGroup->spawn_buff;

                pValue = pElementSon->Attribute("random_num");
                CHECK_NULL(0, pValue);
                pSpawnBuff->random_num = atoi(pValue);

                tinyxml2::XMLElement* pElementBuf = pElementSon->FirstChildElement();
                for (; pElementBuf;
                       pElementBuf = pElementBuf->NextSiblingElement())
                {
                    if (pSpawnBuff->buff.size() >= pSpawnBuff->buff.max_size())
                    {
                        LOGSVR_DEBUG("too many spawn buff:{}" << pSpawnBuff->buff.size());
                        break;
                    }

                    pSpawnBuff->buff.push_back(SpawnOneBuff());
                    SpawnOneBuff* pOneBuff = &pSpawnBuff->buff.back();

                    pValue = pElementBuf->Attribute("id");
                    CHECK_NULL(0, pValue);
                    pOneBuff->id_list.push_back(atoi(pValue));

                    pValue = pElementBuf->Attribute("weight");
                    CHECK_NULL(0, pValue);
                    pOneBuff->weight = atoi(pValue);
                }
            }
            else if (0 == strcmp(pElementSon->Value(), "SpawnNpcs"))
            {
                pValue = pElementSon->Attribute("rand_num_upper");
                if (pValue)
                    pstSpawnGroup->spawn_npcs.random_upper = atoi(pValue);
                pValue = pElementSon->Attribute("rand_num_lower");
                if (pValue)
                    pstSpawnGroup->spawn_npcs.random_lower = atoi(pValue);
                CHECK_EXPR(pstSpawnGroup->spawn_npcs.random_upper >= pstSpawnGroup->spawn_npcs.random_lower, -1, "random_num_upper < random_num_lower ?, map={}", mapDataDesc.map_id);

                pstSpawnGroup->spawn_npcs.spawn_npc_list_start = Instance()->m_iFreeNpc;
                tinyxml2::XMLElement* pElementNpc = pElementSon->FirstChildElement();
                for (; pElementNpc;
                       pElementNpc = pElementNpc->NextSiblingElement())
                {
                    //CHECK_EXPR(pstSpawnGroup->spawn_npcs.SpawnNpcList_size < (int) ARRAYSIZE(pstSpawnGroup->spawn_npcs.SpawnNpcList)
                    //           , -2, "map[ " << mapDataDesc.map_id << "] group[" << pstSpawnGroup->id << "] has too many spawn npc.");
                    CHECK_EXPR(Instance()->m_iFreeNpc < (int)Instance()->m_astSpawnNpcPool.size(), -2, "map[{}] group[{}] has too many spawn npc. ", mapDataDesc.map_id, pstSpawnGroup->id);

                    SpawnNpc* pstNpc = &Instance()->m_astSpawnNpcPool[Instance()->m_iFreeNpc++];

                    int iRet = ReadElementSpawnNpc(mapDataDesc.map_id, pstNpc, pElementNpc);
                    CHECK_EXPR(iRet == 0, -1, "read spawn npc wrong(groupid={})", pstSpawnGroup->id);
                }
                pstSpawnGroup->spawn_npcs.spawn_npc_list_end = Instance()->m_iFreeNpc;
            }
        }
    }
    mapDataDesc.spawn_group_end_idx = Instance()->m_iFreeGroup;

    LOGSVR_TRACE("abyss idx: " << mapDataDesc.abyss_group_idx << " map id: " << mapDataDesc.map_id << " free: " << Instance()->m_iFreeGroup);

    return 0;
}

int MapDataEx::ReadElementStronghold(MapDataDesc& mapDataDesc, tinyxml2::XMLElement* pElement)
{
    return 0;
}

int MapDataEx::ReadElementCrystalData(MapDataDesc& mapDataDesc, tinyxml2::XMLElement* pElement)
{
    return 0;
}

int MapDataEx::ReadElementWeather(MapDataDesc& mapDataDesc, tinyxml2::XMLElement* pElement)
{
    return 0;
}

int MapDataEx::ReadElementBehaviorBox(MapDataDesc& mapDataDesc, tinyxml2::XMLElement* pBehaviorTreeElement)
{
    LOGSVR_TRACE("--begin--");
    int iBehaviorNum = 0;
    const tinyxml2::XMLElement* pElement = pBehaviorTreeElement->FirstChildElement();
    for (; pElement; pElement = pElement->NextSiblingElement())
    {
        CHECK_EXPR(mapDataDesc.behavior_box.size() < mapDataDesc.behavior_box.max_size(), -3, "too many hehavior box for map:{}", mapDataDesc.map_id);
        mapDataDesc.behavior_box.push_back(BoxDesc());
        BoxDesc& box = mapDataDesc.behavior_box.back();

        const char* pValue = pElement->Attribute("BoxID");
        CHECK_NULL(0, pValue);
        box.id = atoi(pValue);

        pValue = pElement->Attribute("bWaitOther");
        if (pValue)
        {
            box.need_wait_all = atoi(pValue);
        }

        pValue = pElement->Attribute("PreEvent");
        if (pValue)
        {
            box.pre_box_id = atoi(pValue);
        }

        pValue = pElement->Attribute("bNoCheckPos");
        if (pValue)
        {
            box.check_collider = atoi(pValue);
        }

        pValue = pElement->Attribute("bCanRepeated");
        if (pValue)
        {
            box.can_repeated = atoi(pValue);
        }
        else
        {
            box.can_repeated = false;
        }

        pValue = pElement->Attribute("Collider");
        CHECK_NULL(0, pValue);
        int iSize = 0;
        CParseUtil::ParseStringToIntArray(pValue, ",", (int*)&box.collider, sizeof(box.collider) / sizeof(int), iSize);
        CHECK_EXPR(iSize == 6, -4, "collider {} not valid", pValue);

        const tinyxml2::XMLElement* pBoxTreeElement = pElement->FirstChildElement();
        for (; pBoxTreeElement;
               pBoxTreeElement = pBoxTreeElement->NextSiblingElement())
        {
            const tinyxml2::XMLElement* pBoxBehaviorElement = pBoxTreeElement->FirstChildElement();
            for (; pBoxBehaviorElement;
                   pBoxBehaviorElement = pBoxBehaviorElement->NextSiblingElement())
            {
                iBehaviorNum++;
                CHECK_EXPR(box.behavior_list.size() < box.behavior_list.max_size(), -4, "too many hehavior list for map:{}", mapDataDesc.map_id);
                box.behavior_list.push_back(BoxBehaviorDesc());
                BoxBehaviorDesc& behavior = box.behavior_list.back();
                if (0 == strcmp(pBoxBehaviorElement->Name(), "AddCharactor") || 0 == strcmp(pBoxBehaviorElement->Name(), "OpenAirWall"))
                {
                    behavior.behavior = E_Box_Behavior_Add_Charactor;

                    pValue = pBoxBehaviorElement->Attribute("id");
                    CHECK_NULL(0, pValue);
                    behavior.id = atoi(pValue);

                    pValue = pBoxBehaviorElement->Attribute("GroupId");
                    CHECK_NULL(0, pValue);
                    std::vector<int> tempInt;
                    NFStringUtility::SplitStringToVectorInt(pValue, ",", tempInt);
                    behavior.param_list = tempInt;
                }
                else if (0 == strcmp(pBoxBehaviorElement->Name(), "RemoveCharactor") || 0 == strcmp(pBoxBehaviorElement->Name(), "CloseAirWall"))
                {
                    behavior.behavior = E_Box_Behavior_Remove_Charactor;

                    pValue = pBoxBehaviorElement->Attribute("id");
                    CHECK_NULL(0, pValue);
                    behavior.id = atoi(pValue);

                    pValue = pBoxBehaviorElement->Attribute("GroupId");
                    CHECK_NULL(0, pValue);
                    std::vector<int> tempInt;
                    NFStringUtility::SplitStringToVectorInt(pValue, ",", tempInt);
                    behavior.param_list = tempInt;
                }
                else if (0 == strcmp(pBoxBehaviorElement->Name(), "CharacterMove"))
                {
                    behavior.behavior = E_Box_Behavior_Teleport;
                    pValue = pBoxBehaviorElement->Attribute("isPathfinding");
                    if (pValue && atoi(pValue))
                    {
                        behavior.behavior = E_Box_Behavior_Character_Move;
                    }

                    pValue = pBoxBehaviorElement->Attribute("id");
                    CHECK_NULL(0, pValue);
                    behavior.id = atoi(pValue);

                    pValue = pBoxBehaviorElement->Attribute("nameInScene");
                    CHECK_NULL(0, pValue);
                    behavior.param_list.push_back(atoi(pValue));

                    pValue = pBoxBehaviorElement->Attribute("Imme");
                    CHECK_NULL(0, pValue);
                    behavior.param_list.push_back(atoi(pValue));

                    pValue = pBoxBehaviorElement->Attribute("targetPosition");
                    CHECK_NULL(0, pValue);

                    std::vector<int> tempInt;
                    NFStringUtility::SplitStringToVectorInt(pValue, ",", tempInt);
                    behavior.param_list = tempInt;
                    behavior.param_list.insert(behavior.param_list.end(), tempInt.begin(), tempInt.end());
                }
                else if (0 == strcmp(pBoxBehaviorElement->Name(), "OpenAI"))
                {
                    behavior.behavior = E_Box_Behavior_Open_AI;
                    pValue = pBoxBehaviorElement->Attribute("id");
                    CHECK_NULL(0, pValue);
                    behavior.id = atoi(pValue);

                    pValue = pBoxBehaviorElement->Attribute("nameInScene");
                    if (pValue)
                        behavior.param_list.push_back(atoi(pValue));
                }
                else if (0 == strcmp(pBoxBehaviorElement->Name(), "CloseAI"))
                {
                    behavior.behavior = E_Box_Behavior_Close_AI;
                    pValue = pBoxBehaviorElement->Attribute("id");
                    CHECK_NULL(0, pValue);
                    behavior.id = atoi(pValue);

                    pValue = pBoxBehaviorElement->Attribute("nameInScene");
                    if (pValue)
                        behavior.param_list.push_back(atoi(pValue));
                }
                else if (0 == strcmp(pBoxBehaviorElement->Name(), "record"))
                {
                    behavior.behavior = E_Box_Behavior_Record;
                    pValue = pBoxBehaviorElement->Attribute("id");
                    CHECK_NULL(0, pValue);
                    behavior.id = atoi(pValue);
                    iBehaviorNum--;
                }
                else if (0 == strcmp(pBoxBehaviorElement->Name(), "SwitchControl"))
                {
                    behavior.behavior = E_Box_Bebavior_Control_Actor;
                    pValue = pBoxBehaviorElement->Attribute("id");
                    CHECK_NULL(0, pValue);
                    behavior.id = atoi(pValue);

                    pValue = pBoxBehaviorElement->Attribute("nameInScene");
                    CHECK_NULL(0, pValue);
                    behavior.param_list.push_back(atoi(pValue));
                }
                else if (0 == strcmp(pBoxBehaviorElement->Name(), "SwitchVision"))
                {
                    behavior.behavior = E_Box_Behavior_Switch_Vision;
                    pValue = pBoxBehaviorElement->Attribute("id");
                    CHECK_NULL(0, pValue);

                    int iRetCode = strcmp(pValue, "partner");
                    if (iRetCode == 0)
                    {
                        LOGSVR_DEBUG("parse partner");

                        behavior.id = -1;
                    }
                    else
                    {
                        behavior.id = atoi(pValue);
                    }

                    pValue = pBoxBehaviorElement->Attribute("hide");
                    CHECK_NULL(0, pValue);
                    behavior.param_list.push_back(atoi(pValue));

                    pValue = pBoxBehaviorElement->Attribute("nameInScene");
                    CHECK_NULL(0, pValue);
                    behavior.param_list.push_back(atoi(pValue));
                }
                else if (0 == strcmp(pBoxBehaviorElement->Name(), "Switch"))
                {
                    behavior.behavior = E_Box_Behavior_Switch;
                    pValue = pBoxBehaviorElement->Attribute("id");
                    CHECK_NULL(0, pValue);
                    behavior.id = atoi(pValue);

                    pValue = pBoxBehaviorElement->Attribute("nameInScene");
                    CHECK_NULL(0, pValue);
                    behavior.param_list.push_back(atoi(pValue));

                    pValue = pBoxBehaviorElement->Attribute("hate");
                    if (pValue)
                    {
                        behavior.param_list.push_back(proto_ff::E_Switch_Behavior_Hate);
                        behavior.param_list.push_back(atoi(pValue));
                    }

                    pValue = pBoxBehaviorElement->Attribute("vision");
                    if (pValue)
                    {
                        behavior.param_list.push_back(proto_ff::E_Switch_Behavior_Vision);
                        behavior.param_list.push_back(atoi(pValue));
                    }

                    pValue = pBoxBehaviorElement->Attribute("ai");
                    if (pValue)
                    {
                        behavior.param_list.push_back(proto_ff::E_Switch_Behavior_AI);
                        behavior.param_list.push_back(atoi(pValue));
                    }

                    pValue = pBoxBehaviorElement->Attribute("through");
                    if (pValue)
                    {
                        behavior.param_list.push_back(proto_ff::E_Switch_Behavior_Through);
                        behavior.param_list.push_back(atoi(pValue));
                    }

                    pValue = pBoxBehaviorElement->Attribute("beHit");
                    if (pValue)
                    {
                        behavior.param_list.push_back(proto_ff::E_Switch_Behavior_Hit_Box);
                        behavior.param_list.push_back(atoi(pValue));
                    }
                }
                else if (0 == strcmp(pBoxBehaviorElement->Name(), "GlobalSwitch"))
                {
                    behavior.behavior = E_Box_Behavior_Global_Switch;
                    pValue = pBoxBehaviorElement->Attribute("id");
                    CHECK_NULL(0, pValue);
                    behavior.id = atoi(pValue);

                    pValue = pBoxBehaviorElement->Attribute("time");
                    if (pValue)
                    {
                        behavior.param_list.push_back(proto_ff::E_Switch_Behavior_Time);
                        behavior.param_list.push_back(atoi(pValue));
                    }

                    pValue = pBoxBehaviorElement->Attribute("platform");
                    if (pValue)
                    {
                        behavior.param_list.push_back(proto_ff::E_Switch_Behavior_Platform);
                        behavior.param_list.push_back(atoi(pValue));
                    }
                }
                else if (0 == strcmp(pBoxBehaviorElement->Name(), "AddBuff"))
                {
                    behavior.behavior = E_Box_Behavior_Add_Buff;
                    pValue = pBoxBehaviorElement->Attribute("id");
                    CHECK_NULL(0, pValue);
                    behavior.id = atoi(pValue);

                    pValue = pBoxBehaviorElement->Attribute("buffID");
                    CHECK_NULL(0, pValue);
                    behavior.param_list.push_back(atoi(pValue));

                    pValue = pBoxBehaviorElement->Attribute("duration");
                    CHECK_NULL(0, pValue);
                    behavior.param_list.push_back(atoi(pValue));

                    pValue = pBoxBehaviorElement->Attribute("nameInScene");
                    CHECK_NULL(0, pValue);
                    behavior.param_list.push_back(atoi(pValue));
                }
                else if (0 == strcmp(pBoxBehaviorElement->Name(), "RemoveBuff"))
                {
                    behavior.behavior = E_Box_Behavior_Remove_Buff;
                    pValue = pBoxBehaviorElement->Attribute("id");
                    CHECK_NULL(0, pValue);
                    behavior.id = atoi(pValue);

                    pValue = pBoxBehaviorElement->Attribute("buffID");
                    CHECK_NULL(0, pValue);
                    behavior.param_list.push_back(atoi(pValue));

                    pValue = pBoxBehaviorElement->Attribute("nameInScene");
                    CHECK_NULL(0, pValue);
                    behavior.param_list.push_back(atoi(pValue));
                }
                else if (0 == strcmp(pBoxBehaviorElement->Name(), "AddTowerLevel"))
                {
                    behavior.behavior = E_Box_Behavior_Set_Tower_Level;
                    pValue = pBoxBehaviorElement->Attribute("id");
                    CHECK_NULL(0, pValue);
                    behavior.id = atoi(pValue);
                }
                else
                {
                    iBehaviorNum--;
                    box.behavior_list.pop_back();
                }
            }
        }
    }

    CHECK_EXPR(iBehaviorNum < MAX_BOX_BEHAVIOR_NUM, -1, "map[{}]  too many behavior num:{}", mapDataDesc.map_id, iBehaviorNum);

    return 0;
}

int MapDataEx::ReadElementLevelCondition(MapDataDesc& mapDataDesc, tinyxml2::XMLElement* pConditionsElement)
{
    LOGSVR_TRACE("--begin--");
    const tinyxml2::XMLElement* pElement = pConditionsElement->FirstChildElement();
    for (; pElement; pElement = pElement->NextSiblingElement())
    {
        CHECK_EXPR(mapDataDesc.level_condition_list.size() < mapDataDesc.level_condition_list.max_size(), -3, "too many level condition [{}] for map {}", mapDataDesc.level_condition_list.size(), mapDataDesc.map_id);
        mapDataDesc.level_condition_list.emplace_back();
        LevelConditionDesc& condition = mapDataDesc.level_condition_list.back();
        const char* pValue = pElement->Attribute("id");
        CHECK_NULL(0, pValue);
        condition.id = atoi(pValue);

        pValue = pElement->Attribute("groupid");
        CHECK_NULL(0, pValue);
        condition.group = atoi(pValue);

        pValue = pElement->Attribute("passTime");
        CHECK_NULL(0, pValue);
        condition.pass_time = atoi(pValue);

        if (0 == strcmp(pElement->Name(), "SomeGroupAppear"))
        {
            condition.condition = E_Level_Condition_Group_Spawn;
        }
        else if (0 == strcmp(pElement->Name(), "SomeMonstersDead"))
        {
            condition.condition = E_Level_Condition_Monster_Dead;
            pValue = pElement->Attribute("monsterNames");
            CHECK_NULL(0, pValue);

            std::vector<int> tempVec;
            NFStringUtility::SplitStringToVectorInt(pValue, ",", tempVec);
            condition.param_list = tempVec;
        }
        else if (0 == strcmp(pElement->Name(), "SomegroupLeft"))
        {
            condition.condition = E_Level_Condition_Group_Left_Count;
            pValue = pElement->Attribute("leftnum");
            CHECK_NULL(0, pValue);
            condition.param_list.push_back(atoi(pValue));
        }
    }

    return 0;
}

int MapDataEx::ReadElementSpawnNpc(int iMapDescID, SpawnNpc* pstNpc, tinyxml2::XMLElement* pElementNpc)
{
    CHECK_NULL(0, pstNpc);
    CHECK_NULL(0, pElementNpc);

    if (pstNpc->ObjType == proto_ff::EN_ACT_TYPE_TOWER ||
        pstNpc->ObjType == proto_ff::EN_ACT_TYPE_CHARGEDUNIT ||
        pstNpc->ObjType == proto_ff::EN_ACT_TYPE_CANNON ||
        pstNpc->ObjType == proto_ff::EN_ACT_TYPE_NEUTRAL_CANNON ||
        pstNpc->ObjType == proto_ff::EN_ACT_TYPE_HOME ||
        pstNpc->ObjType == proto_ff::EN_ACT_TYPE_POWERUNIT ||
        pstNpc->ObjType == proto_ff::EN_ACT_TYPE_MECH)
    {
        LOGSVR_ERROR("map: " << iMapDescID << "invalid npc type: " << pstNpc->ObjType);

        return -1;
    }

    const char* pValue = pElementNpc->Attribute("X");
    CHECK_NULL(0, pValue);
    pstNpc->x = atoi(pValue);

    pValue = pElementNpc->Attribute("Y");
    CHECK_NULL(0, pValue);
    pstNpc->y = atoi(pValue);

    pValue = pElementNpc->Attribute("Z");
    CHECK_NULL(0, pValue);
    pstNpc->z = atoi(pValue);

    pValue = pElementNpc->Attribute("Dir");
    CHECK_NULL(0, pValue);
    pstNpc->dir = atoi(pValue);

    pValue = pElementNpc->Attribute("DescID");
    CHECK_NULL(0, pValue);
    pstNpc->DescID = atoi(pValue);

    pValue = pElementNpc->Attribute("ObjType");
    CHECK_NULL(0, pValue);
    pstNpc->ObjType = atoi(pValue);

    if ((proto_ff::ActorObjType)pstNpc->ObjType == proto_ff::EN_ACT_TYPE_AIR_WALL ||
        (proto_ff::ActorObjType)pstNpc->ObjType == proto_ff::EN_ACT_TYPE_AIR_WALL_NO_LIMIT)
        pstNpc->air_wall_index = g_air_wall_index++;

    pValue = pElementNpc->Attribute("CDQueue");
    CHECK_NULL(0, pValue);
    pstNpc->NumInQueue = atoi(pValue);
    pValue = pElementNpc->Attribute("Camp");
    CHECK_NULL(0, pValue);
    pstNpc->camp = atoi(pValue);

    pValue = pElementNpc->Attribute("fixGround");
    if (pValue)
    {
        pstNpc->fix_ground = atoi(pValue);
    }
    else
        pstNpc->fix_ground = false;

    pValue = pElementNpc->Attribute("nameInEditor");
    pstNpc->name_in_editor = -1;
    if (pValue)
    {
        pstNpc->name_in_editor = atoi(pValue);
    }

    pValue = pElementNpc->Attribute("angleX");
    pstNpc->angle.x = 0;
    if (pValue)
    {
        pstNpc->angle.x = atoi(pValue);
    }

    pValue = pElementNpc->Attribute("angleY");
    pstNpc->angle.y = 0;
    if (pValue)
    {
        pstNpc->angle.y = atoi(pValue);
    }

    pValue = pElementNpc->Attribute("angleZ");
    pstNpc->angle.z = 0;
    if (pValue)
    {
        pstNpc->angle.z = atoi(pValue);
    }

    pValue = pElementNpc->Attribute("level_type");
    if (pValue)
    {
        pstNpc->level_type = atoi(pValue);
    }
    else
        pstNpc->level_type = 0;
    pValue = pElementNpc->Attribute("level_param");
    if (pValue)
    {
        pstNpc->level_param = atoi(pValue);
    }

    pstNpc->is_open_ai = true;
    pValue = pElementNpc->Attribute("isOpenAI");
    if (pValue)
    {
        pstNpc->is_open_ai = atoi(pValue);
    }

    pValue = pElementNpc->Attribute("AI");
    if (pValue)
    {
        pstNpc->ai_filename = pValue;
    }

    pValue = pElementNpc->Attribute("HpThreshold");
    if (pValue)
    {
        pstNpc->hp_threshold = atoi(pValue);
    }
    else
        pstNpc->hp_threshold = -1;
    pValue = pElementNpc->Attribute("ActionStand2");
    if (pValue)
    {
        pstNpc->action_stand2 = pValue;
    }
    pValue = pElementNpc->Attribute("ActionRun2");
    if (pValue)
    {
        pstNpc->action_run2 = pValue;
    }
    pValue = pElementNpc->Attribute("ActionDead2");
    if (pValue)
    {
        pstNpc->action_dead2 = pValue;
    }

    pValue = pElementNpc->Attribute("weight");
    if (pValue)
    {
        pstNpc->random_weight = atoi(pValue);
    }
    else
        pstNpc->random_weight = 100;
    pstNpc->random_weight =
        pstNpc->random_weight > 0 ? pstNpc->random_weight : 100;

    pstNpc->is_born_hide = false;
    pValue = pElementNpc->Attribute("bornHide");
    if (pValue)
    {
        pstNpc->is_born_hide = atoi(pValue);
    }

    pstNpc->delay_born_time = 0;
    pValue = pElementNpc->Attribute("DelayBornTime");
    if (pValue)
    {
        pstNpc->delay_born_time = atoi(pValue);
    }

    pValue = pElementNpc->Attribute("entranceMode");
    if (pValue)
    {
        pstNpc->entrance_mode = atoi(pValue);
    }

    pValue = pElementNpc->Attribute("actionLabel");
    if (pValue)
    {
        pstNpc->action_label = pValue;
    }

    pValue = pElementNpc->Attribute("count_num");
    if (pValue)
    {
        pstNpc->count_num = atoi(pValue);
    }
    else
    {
        pstNpc->count_num = 0;
    }

    pValue = pElementNpc->Attribute("aiGroupID");
    if (pValue)
    {
        pstNpc->host_group_id = atoi(pValue);
    }
    else
    {
        pstNpc->host_group_id = 0;
    }

    //num参数还未确定，暂时不用
    pstNpc->num = 1;

    tinyxml2::XMLElement* pElementSon = pElementNpc->FirstChildElement();
    for (; pElementSon; pElementSon = pElementSon->NextSiblingElement())
    {
        if (0 == strcmp(pElementSon->Value(), "Trigger") && pstNpc->trigger_list.size() < pstNpc->trigger_list.max_size())
        {
            CHECK_EXPR(pstNpc->trigger_list.size() < pstNpc->trigger_list.max_size(), -1, "map[{}] npc[{}], {} has too many trigger.", iMapDescID, pstNpc->DescID, pstNpc->name_in_editor);

            pstNpc->trigger_list.push_back(NpcTriggerDesc());
            NpcTriggerDesc* pNpcTriggerDesc = &pstNpc->trigger_list.back();
            pValue = pElementSon->Attribute("id");
            CHECK_NULL(0, pValue);
            pNpcTriggerDesc->id = atoi(pValue);

            if (0 == strcmp(pElementSon->Attribute("type"), "CapsuleCollider"))
            {
                pValue = pElementSon->Attribute("radius");
                CHECK_NULL(0, pValue);

                pNpcTriggerDesc->collider.length = pNpcTriggerDesc->collider.width = atof(pValue) * VECTOR3_MULTI * 2;

                pValue = pElementSon->Attribute("height");
                CHECK_NULL(0, pValue);
                pNpcTriggerDesc->collider.height = atof(pValue) * VECTOR3_MULTI;
            }
            else
            {
                pValue = pElementSon->Attribute("size");
                CHECK_NULL(0, pValue);
                ParseStringToVector3(pValue, (Proto_Vector3*)&pNpcTriggerDesc->collider.length);
            }

            pValue = pElementSon->Attribute("uniqueId");
            if (pValue)
            {
                pNpcTriggerDesc->unique_id = atoi(pValue);
            }

            pNpcTriggerDesc->operator_obj_type_list.resize(0);
            pValue = pElementSon->Attribute("triggerObjList");
            if (pValue)
            {
                std::vector<int> tempInt;
                NFStringUtility::SplitStringToVectorInt(pValue, ",", tempInt);

                pNpcTriggerDesc->operator_obj_type_list = tempInt;

                for (int i = 0; i < (int)pNpcTriggerDesc->operator_obj_type_list.size(); i++)
                {
                    CHECK_EXPR(proto_ff::ActorObjType_IsValid(pNpcTriggerDesc->operator_obj_type_list[i]), -1, "map[{}] npc[{}, {}] triggerObjList[{}] {} the objtype[{}]", iMapDescID, pstNpc->DescID,
                        pstNpc->name_in_editor, pValue, i, pNpcTriggerDesc->operator_obj_type_list[i]);
                }
            }

            pValue = pElementSon->Attribute("center");
            CHECK_NULL(0, pValue);
            ParseStringToVector3(pValue, &pNpcTriggerDesc->collider.location);
        }
        else if (0 == strcmp(pElementSon->Value(), "SpawnAIParam"))
        {
            SpawnAIParam* pSpawnAIParam = &pstNpc->ai_param;
            pSpawnAIParam->param.resize(0);
            do
            {
                std::string att_name = NF_FORMAT("AI_PARAM_{}", pSpawnAIParam->param.size());
                pValue = pElementSon->Attribute(att_name.c_str());
                if (pValue)
                    pSpawnAIParam->param.push_back(atoi(pValue));
                else
                    pSpawnAIParam->param.push_back(0);
            }
            while (pSpawnAIParam->param.size() < pSpawnAIParam->param.max_size());
        }
        else if (0 == strcmp(pElementSon->Value(), "SpawnBuff"))
        {
            SpawnBuff* pSpawnBuff = &pstNpc->spawn_buff;

            pValue = pElementSon->Attribute("random_num");
            CHECK_NULL(0, pValue);
            pSpawnBuff->random_num = atoi(pValue);

            tinyxml2::XMLElement* pElementBuf = pElementSon->FirstChildElement();
            for (; pElementBuf; pElementBuf = pElementBuf->NextSiblingElement())
            {
                if (pSpawnBuff->buff.size() >= pSpawnBuff->buff.max_size())
                {
                    LOGSVR_DEBUG("too many spawn buff:" << pSpawnBuff->buff.size());
                    break;
                }

                pSpawnBuff->buff.push_back(SpawnOneBuff());
                SpawnOneBuff* pOneBuff = &pSpawnBuff->buff.back();

                pValue = pElementBuf->Attribute("id");
                CHECK_NULL(0, pValue);
                pOneBuff->id_list.push_back(atoi(pValue));

                pValue = pElementBuf->Attribute("weight");
                CHECK_NULL(0, pValue);
                pOneBuff->weight = atoi(pValue);
            }
        }
        else if (0 == strcmp(pElementSon->Value(), "SpawnBuffLib") && pstNpc->buff_lib_list.size() < pstNpc->buff_lib_list.max_size())
        {
            pstNpc->buff_lib_list.push_back(SpawnBuffLib());
            SpawnBuffLib* pBuffLib = &pstNpc->buff_lib_list.back();

            pValue = pElementSon->Attribute("id");
            CHECK_NULL(0, pValue);
            pBuffLib->buff_lib_id = atoi(pValue);

            pValue = pElementSon->Attribute("num");
            CHECK_NULL(0, pValue);
            pBuffLib->spawn_buff_num = atoi(pValue);
        }
        else if (0 == strcmp(pElementSon->Value(), "RandActor") && pstNpc->rand_actor_list.size() < pstNpc->rand_actor_list.max_size())
        {
            pstNpc->rand_actor_list.push_back(SpawnOneActor());
            SpawnOneActor* pOneDesc = &pstNpc->rand_actor_list.back();

            pValue = pElementSon->Attribute("id");
            CHECK_NULL(0, pValue);
            pOneDesc->id = atoi(pValue);

            pValue = pElementSon->Attribute("weight");
            CHECK_NULL(0, pValue);
            pOneDesc->weight = atoi(pValue);

            //这里跟buff的rand不一样， 直接把weight累加
            if (pstNpc->rand_actor_list.size() > 1)
            {
                pOneDesc->weight += pstNpc->rand_actor_list[pstNpc->rand_actor_list.size() - 2].weight;
            }
        }
        else if (0 == strcmp(pElementSon->Value(), "bornPositionOffset"))
        {
            pValue = pElementSon->Attribute("X");
            CHECK_NULL(0, pValue);
            pstNpc->born_pos_offset.x = atoi(pValue);

            pValue = pElementSon->Attribute("Y");
            CHECK_NULL(0, pValue);
            pstNpc->born_pos_offset.y = atoi(pValue);

            pValue = pElementSon->Attribute("Z");
            CHECK_NULL(0, pValue);
            pstNpc->born_pos_offset.z = atoi(pValue);
        }
    }

    return 0;
}
